unit uDZStr;

interface

{$INCLUDE DZCompiler.inc}

uses
  SysUtils, Windows, uDZSysUtils, uDZTypes, uDZAlgorithm;

type
  TCodePage = record
    Name: AnsiString;
    ID: integer;
  end;
  PCodePage = ^TCodePage; 

const
  CODE_PAGE_COUNT = 143;
  NAME_SORTED_CODE_PAGES: array[0..CODE_PAGE_COUNT - 1] of TCodePage =
  ((Name: 'IBM037'; ID: 37),
    (Name: 'IBM437'; ID: 437),
    (Name: 'IBM500'; ID: 500),
    (Name: 'ASMO-708'; ID: 708),
    (Name: 'ASMO-449+'; ID: 709),
    (Name: 'BCON V4'; ID: 709),
    (Name: 'Arabic'; ID: 710),
    (Name: 'DOS-720'; ID: 720),
    (Name: 'ibm737'; ID: 737),
    (Name: 'ibm775'; ID: 775), 
    (Name: 'ibm850'; ID: 850),
    (Name: 'ibm852'; ID: 852),
    (Name: 'IBM855'; ID: 855),
    (Name: 'ibm857'; ID: 857),
    (Name: 'IBM00858'; ID: 858),
    (Name: 'IBM860'; ID: 860),
    (Name: 'ibm861'; ID: 861),
    (Name: 'DOS-862'; ID: 862),
    (Name: 'IBM863'; ID: 863),
    (Name: 'IBM864'; ID: 864),
    (Name: 'IBM865'; ID: 865),
    (Name: 'cp866'; ID: 866),
    (Name: 'ibm869'; ID: 869),
    (Name: 'IBM870'; ID: 870),
    (Name: 'windows-874'; ID: 874),
    (Name: 'cp875'; ID: 875),
    (Name: 'shift_jis'; ID: 932),
    (Name: 'gb2312'; ID: 936),
    (Name: 'ks_c_5601-1987'; ID: 949),
    (Name: 'big5'; ID: 950),
    (Name: 'IBM1026'; ID: 1026),
    (Name: 'IBM01047'; ID: 1047),
    (Name: 'IBM01140'; ID: 1140),
    (Name: 'IBM01141'; ID: 1141),
    (Name: 'IBM01142'; ID: 1142),
    (Name: 'IBM01143'; ID: 1143),
    (Name: 'IBM01144'; ID: 1144),
    (Name: 'IBM01145'; ID: 1145),
    (Name: 'IBM01146'; ID: 1146),
    (Name: 'IBM01147'; ID: 1147),
    (Name: 'IBM01148'; ID: 1148),
    (Name: 'IBM01149'; ID: 1149),
    (Name: 'utf-16'; ID: 1200),
    (Name: 'unicodeFFFE'; ID: 1201),
    (Name: 'windows-1250'; ID: 1250),
    (Name: 'windows-1251'; ID: 1251),
    (Name: 'windows-1252'; ID: 1252),
    (Name: 'windows-1253'; ID: 1253),
    (Name: 'windows-1254'; ID: 1254),
    (Name: 'windows-1255'; ID: 1255),
    (Name: 'windows-1256'; ID: 1256),
    (Name: 'windows-1257'; ID: 1257),
    (Name: 'windows-1258'; ID: 1258),
    (Name: 'Johab'; ID: 1361),
    (Name: 'macintosh'; ID: 10000),
    (Name: 'x-mac-japanese'; ID: 10001),
    (Name: 'x-mac-chinesetrad'; ID: 10002),
    (Name: 'x-mac-korean'; ID: 10003),
    (Name: 'x-mac-arabic'; ID: 10004),
    (Name: 'x-mac-hebrew'; ID: 10005),
    (Name: 'x-mac-greek'; ID: 10006),
    (Name: 'x-mac-cyrillic'; ID: 10007),
    (Name: 'x-mac-chinesesimp'; ID: 10008),
    (Name: 'x-mac-romanian'; ID: 10010),
    (Name: 'x-mac-ukrainian'; ID: 10017),
    (Name: 'x-mac-thai'; ID: 10021),
    (Name: 'x-mac-ce'; ID: 10029),
    (Name: 'x-mac-icelandic'; ID: 10079),
    (Name: 'x-mac-turkish'; ID: 10081),
    (Name: 'x-mac-croatian'; ID: 10082),
    (Name: 'utf-32'; ID: 12000),
    (Name: 'utf-32BE'; ID: 12001),
    (Name: 'x-Chinese_CNS'; ID: 20000),
    (Name: 'x-cp20001'; ID: 20001),
    (Name: 'x_Chinese-Eten'; ID: 20002),
    (Name: 'x-cp20003'; ID: 20003),
    (Name: 'x-cp20004'; ID: 20004),
    (Name: 'x-cp20005'; ID: 20005),
    (Name: 'x-IA5'; ID: 20105),
    (Name: 'x-IA5-German'; ID: 20106),
    (Name: 'x-IA5-Swedish'; ID: 20107),
    (Name: 'x-IA5-Norwegian'; ID: 20108),
    (Name: 'us-ascii'; ID: 20127),
    (Name: 'x-cp20261'; ID: 20261),
    (Name: 'x-cp20269'; ID: 20269),
    (Name: 'IBM273'; ID: 20273),
    (Name: 'IBM277'; ID: 20277),
    (Name: 'IBM278'; ID: 20278),
    (Name: 'IBM280'; ID: 20280),
    (Name: 'IBM284'; ID: 20284),
    (Name: 'IBM285'; ID: 20285),
    (Name: 'IBM290'; ID: 20290),
    (Name: 'IBM297'; ID: 20297),
    (Name: 'IBM420'; ID: 20420),
    (Name: 'IBM423'; ID: 20423),
    (Name: 'IBM424'; ID: 20424),
    (Name: 'x-EBCDIC-KoreanExtended'; ID: 20833),
    (Name: 'IBM-Thai'; ID: 20838),
    (Name: 'koi8-r'; ID: 20866),
    (Name: 'IBM871'; ID: 20871),
    (Name: 'IBM880'; ID: 20880),
    (Name: 'IBM905'; ID: 20905),
    (Name: 'IBM00924'; ID: 20924),
    (Name: 'EUC-JP'; ID: 20932),
    (Name: 'x-cp20936'; ID: 20936),
    (Name: 'x-cp20949'; ID: 20949),
    (Name: 'cp1025'; ID: 21025),
    (Name: 'koi8-u'; ID: 21866),
    (Name: 'iso-8859-1'; ID: 28591),
    (Name: 'iso-8859-2'; ID: 28592),
    (Name: 'iso-8859-3'; ID: 28593),
    (Name: 'iso-8859-4'; ID: 28594),
    (Name: 'iso-8859-5'; ID: 28595),
    (Name: 'iso-8859-6'; ID: 28596),
    (Name: 'iso-8859-7'; ID: 28597),
    (Name: 'iso-8859-8'; ID: 28598),
    (Name: 'iso-8859-9'; ID: 28599),
    (Name: 'iso-8859-13'; ID: 28603),
    (Name: 'iso-8859-15'; ID: 28605),
    (Name: 'x-Europa'; ID: 29001),
    (Name: 'iso-8859-8-i'; ID: 38598),
    (Name: 'iso-2022-jp'; ID: 50220),
    (Name: 'csISO2022JP'; ID: 50221),
    (Name: 'iso-2022-jp'; ID: 50222),
    (Name: 'iso-2022-kr'; ID: 50225),
    (Name: 'x-cp50227'; ID: 50227),
    (Name: 'euc-jp'; ID: 51932),
    (Name: 'EUC-CN'; ID: 51936),
    (Name: 'euc-kr'; ID: 51949),
    (Name: 'hz-gb-2312'; ID: 52936),
    (Name: 'GB18030'; ID: 54936),
    (Name: 'x-iscii-de'; ID: 57002),
    (Name: 'x-iscii-be'; ID: 57003),
    (Name: 'x-iscii-ta'; ID: 57004),
    (Name: 'x-iscii-te'; ID: 57005),
    (Name: 'x-iscii-as'; ID: 57006),
    (Name: 'x-iscii-or'; ID: 57007),
    (Name: 'x-iscii-ka'; ID: 57008),
    (Name: 'x-iscii-ma'; ID: 57009),
    (Name: 'x-iscii-gu'; ID: 57010),
    (Name: 'x-iscii-pa'; ID: 57011),
    (Name: 'utf-7'; ID: 65000),
    (Name: 'utf-8'; ID: 65001));

  ID_SORTED_CODE_PAGES: array[0..CODE_PAGE_COUNT - 1] of TCodePage =
  ((Name: 'IBM037'; ID: 37),
    (Name: 'IBM437'; ID: 437),
    (Name: 'IBM500'; ID: 500),
    (Name: 'ASMO-708'; ID: 708),
    (Name: 'ASMO-449+'; ID: 709),
    (Name: 'BCON V4'; ID: 709),
    (Name: 'Arabic'; ID: 710),
    (Name: 'DOS-720'; ID: 720),
    (Name: 'ibm737'; ID: 737),
    (Name: 'ibm775'; ID: 775), 
    (Name: 'ibm850'; ID: 850),
    (Name: 'ibm852'; ID: 852),
    (Name: 'IBM855'; ID: 855),
    (Name: 'ibm857'; ID: 857),
    (Name: 'IBM00858'; ID: 858),
    (Name: 'IBM860'; ID: 860),
    (Name: 'ibm861'; ID: 861),
    (Name: 'DOS-862'; ID: 862),
    (Name: 'IBM863'; ID: 863),
    (Name: 'IBM864'; ID: 864),
    (Name: 'IBM865'; ID: 865),
    (Name: 'cp866'; ID: 866),
    (Name: 'ibm869'; ID: 869),
    (Name: 'IBM870'; ID: 870),
    (Name: 'windows-874'; ID: 874),
    (Name: 'cp875'; ID: 875),
    (Name: 'shift_jis'; ID: 932),
    (Name: 'gb2312'; ID: 936),
    (Name: 'ks_c_5601-1987'; ID: 949),
    (Name: 'big5'; ID: 950),
    (Name: 'IBM1026'; ID: 1026),
    (Name: 'IBM01047'; ID: 1047),
    (Name: 'IBM01140'; ID: 1140),
    (Name: 'IBM01141'; ID: 1141),
    (Name: 'IBM01142'; ID: 1142),
    (Name: 'IBM01143'; ID: 1143),
    (Name: 'IBM01144'; ID: 1144),
    (Name: 'IBM01145'; ID: 1145),
    (Name: 'IBM01146'; ID: 1146),
    (Name: 'IBM01147'; ID: 1147),
    (Name: 'IBM01148'; ID: 1148),
    (Name: 'IBM01149'; ID: 1149),
    (Name: 'utf-16'; ID: 1200),
    (Name: 'unicodeFFFE'; ID: 1201),
    (Name: 'windows-1250'; ID: 1250),
    (Name: 'windows-1251'; ID: 1251),
    (Name: 'windows-1252'; ID: 1252),
    (Name: 'windows-1253'; ID: 1253),
    (Name: 'windows-1254'; ID: 1254),
    (Name: 'windows-1255'; ID: 1255),
    (Name: 'windows-1256'; ID: 1256),
    (Name: 'windows-1257'; ID: 1257),
    (Name: 'windows-1258'; ID: 1258),
    (Name: 'Johab'; ID: 1361),
    (Name: 'macintosh'; ID: 10000),
    (Name: 'x-mac-japanese'; ID: 10001),
    (Name: 'x-mac-chinesetrad'; ID: 10002),
    (Name: 'x-mac-korean'; ID: 10003),
    (Name: 'x-mac-arabic'; ID: 10004),
    (Name: 'x-mac-hebrew'; ID: 10005),
    (Name: 'x-mac-greek'; ID: 10006),
    (Name: 'x-mac-cyrillic'; ID: 10007),
    (Name: 'x-mac-chinesesimp'; ID: 10008),
    (Name: 'x-mac-romanian'; ID: 10010),
    (Name: 'x-mac-ukrainian'; ID: 10017),
    (Name: 'x-mac-thai'; ID: 10021),
    (Name: 'x-mac-ce'; ID: 10029),
    (Name: 'x-mac-icelandic'; ID: 10079),
    (Name: 'x-mac-turkish'; ID: 10081),
    (Name: 'x-mac-croatian'; ID: 10082),
    (Name: 'utf-32'; ID: 12000),
    (Name: 'utf-32BE'; ID: 12001),
    (Name: 'x-Chinese_CNS'; ID: 20000),
    (Name: 'x-cp20001'; ID: 20001),
    (Name: 'x_Chinese-Eten'; ID: 20002),
    (Name: 'x-cp20003'; ID: 20003),
    (Name: 'x-cp20004'; ID: 20004),
    (Name: 'x-cp20005'; ID: 20005),
    (Name: 'x-IA5'; ID: 20105),
    (Name: 'x-IA5-German'; ID: 20106),
    (Name: 'x-IA5-Swedish'; ID: 20107),
    (Name: 'x-IA5-Norwegian'; ID: 20108),
    (Name: 'us-ascii'; ID: 20127),
    (Name: 'x-cp20261'; ID: 20261),
    (Name: 'x-cp20269'; ID: 20269),
    (Name: 'IBM273'; ID: 20273),
    (Name: 'IBM277'; ID: 20277),
    (Name: 'IBM278'; ID: 20278),
    (Name: 'IBM280'; ID: 20280),
    (Name: 'IBM284'; ID: 20284),
    (Name: 'IBM285'; ID: 20285),
    (Name: 'IBM290'; ID: 20290),
    (Name: 'IBM297'; ID: 20297),
    (Name: 'IBM420'; ID: 20420),
    (Name: 'IBM423'; ID: 20423),
    (Name: 'IBM424'; ID: 20424),
    (Name: 'x-EBCDIC-KoreanExtended'; ID: 20833),
    (Name: 'IBM-Thai'; ID: 20838),
    (Name: 'koi8-r'; ID: 20866),
    (Name: 'IBM871'; ID: 20871),
    (Name: 'IBM880'; ID: 20880),
    (Name: 'IBM905'; ID: 20905),
    (Name: 'IBM00924'; ID: 20924),
    (Name: 'EUC-JP'; ID: 20932),
    (Name: 'x-cp20936'; ID: 20936),
    (Name: 'x-cp20949'; ID: 20949),
    (Name: 'cp1025'; ID: 21025),
    (Name: 'koi8-u'; ID: 21866),
    (Name: 'iso-8859-1'; ID: 28591),
    (Name: 'iso-8859-2'; ID: 28592),
    (Name: 'iso-8859-3'; ID: 28593),
    (Name: 'iso-8859-4'; ID: 28594),
    (Name: 'iso-8859-5'; ID: 28595),
    (Name: 'iso-8859-6'; ID: 28596),
    (Name: 'iso-8859-7'; ID: 28597),
    (Name: 'iso-8859-8'; ID: 28598),
    (Name: 'iso-8859-9'; ID: 28599),
    (Name: 'iso-8859-13'; ID: 28603),
    (Name: 'iso-8859-15'; ID: 28605),
    (Name: 'x-Europa'; ID: 29001),
    (Name: 'iso-8859-8-i'; ID: 38598),
    (Name: 'iso-2022-jp'; ID: 50220),
    (Name: 'csISO2022JP'; ID: 50221),
    (Name: 'iso-2022-jp'; ID: 50222),
    (Name: 'iso-2022-kr'; ID: 50225),
    (Name: 'x-cp50227'; ID: 50227),
    (Name: 'euc-jp'; ID: 51932),
    (Name: 'EUC-CN'; ID: 51936),
    (Name: 'euc-kr'; ID: 51949),
    (Name: 'hz-gb-2312'; ID: 52936),
    (Name: 'GB18030'; ID: 54936),
    (Name: 'x-iscii-de'; ID: 57002),
    (Name: 'x-iscii-be'; ID: 57003),
    (Name: 'x-iscii-ta'; ID: 57004),
    (Name: 'x-iscii-te'; ID: 57005),
    (Name: 'x-iscii-as'; ID: 57006),
    (Name: 'x-iscii-or'; ID: 57007),
    (Name: 'x-iscii-ka'; ID: 57008),
    (Name: 'x-iscii-ma'; ID: 57009),
    (Name: 'x-iscii-gu'; ID: 57010),
    (Name: 'x-iscii-pa'; ID: 57011),
    (Name: 'utf-7'; ID: 65000),
    (Name: 'utf-8'; ID: 65001));
    
type
  TAnsiCharSet = set of AnsiChar;
  TAnsiCharRange = object
  public
    StrBegin: PAnsiChar;
    StrEnd: PAnsiChar;
    function AsString: AnsiString;
  end;

  TWideCharRange = object
  public
    StrBegin: PWideChar;
    StrEnd: PWideChar;
    function AsString: WideString;
  end;

function AnsiSkipChar(Str, StrEnd: PAnsiChar; Chars: TAnsiCharSet = [#32]): PAnsiChar; overload;
function AnsiSeekChar(Str, StrEnd: PAnsiChar; Chars: TAnsiCharSet = [#32]): PAnsiChar; overload;
function AnsiSkipChar(Str, StrEnd: PAnsiChar; c: AnsiChar): PAnsiChar; overload;
function AnsiSeekChar(Str, StrEnd: PAnsiChar; c: AnsiChar): PAnsiChar; overload;

function WideSkipChar(Str, StrEnd: PWideChar; Chars: WideString): PWideChar; overload;
function WideSeekChar(Str, StrEnd: PWideChar; Chars: WideString): PWideChar; overload;
function WideSkipChar(Str, StrEnd: PWideChar; c: WideChar): PWideChar; overload;
function WideSeekChar(Str, StrEnd: PWideChar; c: WideChar): PWideChar; overload;

function ReverseSeekChar(Str, StrEnd: PAnsiChar;
  Chars: TAnsiCharSet = [#32]): PAnsiChar;

procedure AnsiLower(Str: PAnsiChar; Len: Integer);
procedure AnsiUpper(Str: PAnsiChar; Len: Integer);
procedure WideLower(Str: PWideChar; Len: Integer);
procedure WideUpper(Str: PWideChar; Len: Integer);

{$IFDEF UNICODE}
function tcsprintf(Output: PWideChar; Format: PWideChar): Integer; cdecl; varargs;
{$ELSE}
function tcsprintf(Output: PAnsiChar; Format: PAnsiChar): Integer; cdecl; varargs;
{$ENDIF}
function sprintf(Output: PAnsiChar; Format: PAnsiChar): Integer; cdecl; varargs;
function wcsprintf(Output: PWideChar; Format: PWideChar): Integer; cdecl; varargs;

function AnsiStrDuplicate(const s: AnsiString): AnsiString;
function WiDestrDuplicate(const s: WiDestring): WiDestring;

function AnsiCompareString(const Str1, Str2: AnsiString;
  Options: TStringCompareOptions = [];
  Locale: TLocale = loUserDefault): TCompareResult;

function WideCompareString(const Str1, Str2: WideString;
  Options: TStringCompareOptions = [];
  Locale: TLocale = loUserDefault): TCompareResult;

function AnsiCompare(Str1: PAnsiChar; Len1: Integer; Str2: PAnsiChar; Len2: Integer;
  Options: TStringCompareOptions = []; Locale: TLocale = loUserDefault): TCompareResult;

function WideCompare(Str1: PWideChar; Len1: Integer; Str2: PWideChar; Len2: Integer;
  Options: TStringCompareOptions = []; Locale: TLocale = loUserDefault): TCompareResult;

function UnicodeToUtf8(Src: PWideChar; SrcLen: TUnsignedLong;
  dest: PAnsiChar; destLen: TUnsignedLong): TUnsignedLong;

function Utf8ToUnicode(Src: PAnsiChar; SrcLen: TUnsignedLong;
  dest: PWideChar; destLen: TUnsignedLong): TUnsignedLong;

function Utf8Encode(const s: WideString): UTF8String;
function Utf8Decode(const s: UTF8String): WideString;
function AnsiToUtf8(const s: AnsiString): UTF8String;
function Utf8ToAnsi(const s: UTF8String): AnsiString;

function wctomb(Src: PWideChar; SrcLen: TUnsignedLong; dest: PAnsiChar;
  destLen: TUnsignedLong; CodePage: Integer = CP_ACP): TUnsignedLong;
function UnicodeToAnsi(const s: WideString; CodePage: Integer = CP_ACP): AnsiString;
function mbtowc(Src: PAnsiChar; SrcLen: TUnsignedLong; dest: PWideChar;
  destLen: TUnsignedLong; CodePage: Integer = CP_ACP): TUnsignedLong;
function AnsiToUnicode(const s: AnsiString; CodePage: Integer = CP_ACP): WideString;

function RandomAnsiString(CharTable: PAnsiChar; CharCount: Integer;
  ResultLength: Integer): AnsiString;

function RandomWideString(CharTable: PWideChar; CharCount: Integer;
  ResultLength: Integer): WideString;

function RandomString(CharTable: PChar; CharCount: Integer;
  ResultLength: Integer): string;

function AnsiStrFromBuf(Buf: Pointer; BufLen: Integer): AnsiString;

function WideStrFromBuf(Buf: Pointer; BufLen: Integer): WideString;

function GetCodePageID(Name: PAnsiChar; NameLen: Integer): Integer; overload;
function GetCodePageID(const Name: AnsiString): Integer; overload;
function GetCodePageName(ID: Integer): AnsiString; overload;

type
  TWideStrRefRec = object
    RefCount: Longint;
    CharCount: LongInt;
    CharArray: array[0..0] of WideChar;
  end;
  PWideStrRefRec = ^TWideStrRefRec;

  PRefWideString = ^TRefWideString;
  TRefWideString = object
  private
    RefRec: AnsiString;
    function GetRefRec: PWideStrRefRec;
    procedure Write(From: Integer; Buf: PWideChar; Size: Integer);
    procedure CopyOnWrite(NewLen: Integer = -1; CopyPos: Integer = 1);
  public
    function AsWideStr: WideString;
    function AsBuffer: PWideChar;
    function AsAnsiStr: AnsiString;
    function AsUTF8Str: AnsiString;
    function Empty: Boolean;
    function GetLength: Integer;
    procedure SetLength(NewLen: Integer);
    function GetAt(Index: Integer): WideChar;
    procedure SetAt(Index: Integer; Value: WideChar);
    procedure Append(Str: PWideChar; Size: Integer); overload;
    procedure Append(Str: PRefWideString); overload;
    procedure Append(const Str: WideString); overload;
    procedure Insert(Str: PWideChar; Size: Integer); overload;
    procedure Insert(Str: PRefWideString); overload;
    procedure Insert(const Str: WideString); overload;
    procedure Assign(Str: PWideChar; Size: Integer); overload;
    procedure Assign(const Str: TRefWideString); overload;
    procedure Assign(const Str: WideString); overload;
    procedure Assign(const Str: AnsiString); overload;
    procedure Assign(Str: PAnsiChar; Size: Integer); overload;
    procedure AssignUTF8(const Str: UTF8String); overload;
    procedure AssignUTF8(Str: PAnsiChar; Size: Integer); overload;
    function Copy(Index, Count: Integer): TRefWideString;
    procedure Delete(Index: Integer; Count: Integer = 1);
  end;

{$ifndef DELPHI_2009_OR_HIGHER}
  UnicodeString = TRefWideString;
{$endif}

  TDzAnsiStringBuilder = object
  private
    fDataString: AnsiString;
    fPosition: Integer;
    procedure SetDataString(const Value: AnsiString);
    procedure setLength(newLen: Integer);
    function getLength: Integer;
    procedure setPosition(const Value: Integer);
  public
    procedure init;
    procedure uninit;
    procedure WriteInt(value: Integer);
    procedure WriteString(const str: AnsiString);
    procedure WriteBuffer(buf: PAnsiChar; len: Integer);
    procedure FormatWrite(const fmt: AnsiString; const args: array of const);
    procedure FormatWriteBuf(const fmt: PAnsiChar; FmtLen: Integer; args: array of const);
    property DataString: AnsiString read fDataString write SetDataString;
    property Length: Integer read getLength write setLength;
    property Position: Integer read fPosition write setPosition;
  end;

implementation

{$IFDEF UNICODE}
function tcsprintf; external user32 name 'wsprintfW';
{$ELSE}
function tcsprintf; external user32 name 'wsprintfA';
{$ENDIF}
function sprintf; external user32 name 'wsprintfA';
function wcsprintf; external user32 name 'wsprintfW';

function AnsiSkipChar(Str, StrEnd: PAnsiChar; Chars: TAnsiCharSet = [#32]): PAnsiChar;
begin
  while (Str < StrEnd) and (Str^ in Chars) do Inc(Str);
  Result := Str;
end;

function AnsiSkipChar(Str, StrEnd: PAnsiChar; c: AnsiChar): PAnsiChar;
begin
  while (Str < StrEnd) and (Str^ = c) do Inc(Str);
  Result := Str;
end;

function AnsiSeekChar(Str, StrEnd: PAnsiChar; Chars: TAnsiCharSet = [#32]): PAnsiChar;
begin
  while (Str < StrEnd) and not (Str^ in Chars) do Inc(Str);
  Result := Str;
end;

function AnsiSeekChar(Str, StrEnd: PAnsiChar; c: AnsiChar): PAnsiChar;
begin
  while (Str < StrEnd) and (Str^ <> c) do Inc(Str);
  Result := Str;
end;

function WideSkipChar(Str, StrEnd: PWideChar; Chars: WideString): PWideChar;
var
  i: Integer;
  Flag: Boolean;
begin
  Result := Str;
  while Result < StrEnd do
  begin
    Flag := True;
    for i := 1 to Length(Chars) do
    begin
      if Result^ = Chars[i] then begin Flag := False; Break;
      end;
    end;
    if Flag then Exit;
    Inc(Result);
  end;
end;

function WideSkipChar(Str, StrEnd: PWideChar; c: WideChar): PWideChar;
begin
  while (Str < StrEnd) and (Str^ = c) do Inc(Str);
  Result := Str;
end;

function WideSeekChar(Str, StrEnd: PWideChar; Chars: WideString): PWideChar;
var
  i: Integer;
begin
  Result := Str;
  while Result < StrEnd do
  begin
    for i := 1 to Length(Chars) do
      if Result^ = Chars[i] then Exit;
    Inc(Result);
  end;
end;

function WideSeekChar(Str, StrEnd: PWideChar; c: WideChar): PWideChar;
begin
  while (Str < StrEnd) and (Str^ <> c) do Inc(Str);
  Result := Str;
end;


procedure AnsiLower(Str: PAnsiChar; Len: Integer);
var
  i: Integer;
begin
  for i := 0 to Len - 1 do
  begin
    Str^ := CASE_CHAR_TABLE[ccLower][Str^];
    //if Str^ in ['A'..'Z'] then Str^ := AnsiChar(PByte(Str)^ or $20);
    Inc(Str);
  end;
end;

procedure AnsiUpper(Str: PAnsiChar; Len: Integer);
var
  i: Integer;
begin
  for i := 0 to Len - 1 do
  begin
    Str^ := CASE_CHAR_TABLE[ccUpper][Str^];
    //if Str^ in ['a'..'z'] then Str^ := AnsiChar(PByte(Str)^ and not $20);
    Inc(Str);
  end;
end;

procedure WideLower(Str: PWideChar; Len: Integer);
var
  i: Integer;
begin
  for i := 0 to Len - 1 do
  begin
    if (Str^ >= 'A') and (Str^ <= 'Z') then Str^ := WideChar(PWord(Str)^ or $20);
    Inc(Str);
  end;
end;

procedure WideUpper(Str: PWideChar; Len: Integer);
var
  i: Integer;
begin
  for i := 0 to Len - 1 do
  begin
    if (Str^ >= 'a') and (Str^ <= 'z') then Str^ := WideChar(PWord(Str)^ and not $20);
    Inc(Str);
  end;
end;

function ReverseSeekChar(Str, StrEnd: PAnsiChar;
  Chars: TAnsiCharSet = [#32]): PAnsiChar;
begin
  Dec(StrEnd);
  while (Str <= StrEnd) and not (StrEnd^ in Chars) do Dec(StrEnd);
  Result := StrEnd;
end;

function AnsiStrFromBuf(Buf: Pointer; BufLen: Integer): AnsiString;
begin
  SetLength(Result, BufLen);
  if BufLen = 0 then Exit;
  Move(Buf^, Pointer(Result)^, BufLen);
end;

function WideStrFromBuf(Buf: Pointer; BufLen: Integer): WideString;
begin
  SetLength(Result, BufLen div 2);
  Move(Buf^, Pointer(Result)^, BufLen);
end;

function RandomAnsiString(CharTable: PAnsiChar; CharCount: Integer;
  ResultLength: Integer): AnsiString;
var
  i: Integer;
begin
  SetLength(Result, ResultLength);
  for i := 1 to ResultLength do
    Result[i] := CharTable[Random(CharCount)];
end;

function RandomWideString(CharTable: PWideChar; CharCount: Integer;
  ResultLength: Integer): WideString;
var
  i: Integer;
begin
  SetLength(Result, ResultLength);
  for i := 1 to ResultLength do
    Result[i] := CharTable[Random(CharCount)];
end;

function RandomString(CharTable: PChar; CharCount: Integer;
  ResultLength: Integer): string;
var
  i: Integer;
begin
  SetLength(Result, ResultLength);
  for i := 1 to ResultLength do
    Result[i] := CharTable[Random(CharCount)];
end;

function AnsiStrDuplicate(const s: AnsiString): AnsiString;
var
  Len: Integer;
begin
  Len := Length(s);
  SetLength(Result, Len);
  Move(Pointer(s)^, Pointer(Result)^, Len);
end;

function WiDestrDuplicate(const s: WiDestring): WiDestring;
var
  Len: Integer;
begin
  Len := Length(s);
  SetLength(Result, Len);
  Move(Pointer(s)^, Pointer(Result)^, SizeOf(WideChar) * Len);
end;

function TranslateCompareStringFlags(Options: TStringCompareOptions): LongWord;
var
  opt: TStringCompareOption;
begin
  Result := 0;
  for opt := Low(TStringCompareOption) to High(TStringCompareOption) do
  begin
    if (opt in Options) then Result := Result or COMPARE_StrING_FLAGS[opt];
  end;
end;

function ParseWinAPICompareStringReturnValue(
  RetValue: Integer): TCompareResult;
begin
  if (RetValue - 2 > 0) then Result := crGreater
  else if (RetValue - 2 = 0) then Result := crEqual
  else Result := crLess;
end;

function AnsiCompareString(const Str1, Str2: AnsiString; Options: TStringCompareOptions;
  Locale: TLocale): TCompareResult;
begin
  if (Pointer(Str1) = Pointer(Str2)) then
  begin
    Result := crEqual;
    Exit;
  end;
  Result := ParseWinAPICompareStringReturnValue(CompareStringA(
    Locale_IDS[Locale],
    TranslateCompareStringFlags(Options),
    PAnsiChar(Str1),
    Length(Str1),
    PAnsiChar(Str2),
    Length(Str2)));
end;

function AnsiCompare(Str1: PAnsiChar; Len1: Integer; Str2: PAnsiChar; Len2: Integer;
  Options: TStringCompareOptions; Locale: TLocale): TCompareResult;
begin
  if (Str1 = Str2) then
  begin
    Result := crEqual;
    Exit;
  end;
  Result := ParseWinAPICompareStringReturnValue(CompareStringA(Locale_IDS[Locale],
    TranslateCompareStringFlags(Options),
    PAnsiChar(Str1), Len1, PAnsiChar(Str2), Len2));
end;

function WideCompareString(const Str1, Str2: WideString; Options: TStringCompareOptions;
  Locale: TLocale): TCompareResult;
begin
  if (Pointer(Str1) = Pointer(Str2)) then
  begin
    Result := crEqual;
    Exit;
  end;
  Result := ParseWinAPICompareStringReturnValue(CompareStringW(Locale_IDS[Locale],
    TranslateCompareStringFlags(Options),
    PWideChar(Str1), Length(Str1), PWideChar(Str2), Length(Str2)))
end;

function WideCompare(Str1: PWideChar; Len1: Integer; Str2: PWideChar; Len2: Integer;
  Options: TStringCompareOptions; Locale: TLocale): TCompareResult;
begin
  if Str1 = Str2 then
  begin
    Result := crEqual;
    Exit;
  end;
  Result := ParseWinAPICompareStringReturnValue(CompareStringW(Locale_IDS[Locale],
    TranslateCompareStringFlags(Options),
    PWideChar(Str1), Len1, PWideChar(Str2), Len2));
end;

function UnicodeToUtf8(Src: PWideChar; SrcLen: TUnsignedLong;
  dest: PAnsiChar; destLen: TUnsignedLong): TUnsignedLong;
var
  i, count: TUnsignedLong;
  c: TUnsignedLong;
begin
  if Src = nil then
  begin
    Result := 0;
    Exit;
  end;
  count := 0;
  i := 0;
  if dest <> nil then
  begin
    while (i < SrcLen) and (count < destLen) do
    begin
      c := TUnsignedLong(Src[i]);
      Inc(i);
      if c <= $7F then
      begin
        Dest[count] := AnsiChar(c);
        Inc(count);
      end
      else if c > $7FF then
      begin
        if count + 3 > destLen then break;
        dest[count] := AnsiChar($E0 or (c shr 12));
        dest[count + 1] := AnsiChar($80 or ((c shr 6) and $3F));
        dest[count + 2] := AnsiChar($80 or (c and $3F));
        Inc(count, 3);
      end
      else begin
        //  $7F < Src[i] <= $7FF
        if count + 2 > destLen then break;
        dest[count] := AnsiChar($C0 or (c shr 6));
        dest[count + 1] := AnsiChar($80 or (c and $3F));
        Inc(count, 2);
      end;
    end;
    //if (count >= destLen) then count := destLen - 1;
    //dest[count] := #0;
  end
  else begin
    while i < SrcLen do
    begin
      c := TUnsignedLong(Src[i]);
      Inc(i);
      if c > $7F then
      begin
        if c > $7FF then Inc(count);
        Inc(count);
      end;
      Inc(count);
    end;
  end;
  Result := count;
end;

function Utf8ToUnicode(Src: PAnsiChar; SrcLen: TUnsignedLong;
  dest: PWideChar; destLen: TUnsignedLong): TUnsignedLong;
var
  i, count: TUnsignedLong;
  c: Byte;
  wc: TUnsignedLong;
begin
  if Src = nil then
  begin
    Result := 0;
    Exit;
  end;
  Result := TUnsignedLong(-1);
  count := 0;
  i := 0;
  if dest <> nil then
  begin
    while (i < SrcLen) and (count < destLen) do
    begin
      wc := TUnsignedLong(Src[i]);
      Inc(i);
      if (wc and $80) <> 0 then
      begin
        (* incomplete multibyte char *)
        if i >= SrcLen then Exit;

        wc := wc and $3F;
        if (wc and $20) <> 0 then
        begin
          c := Byte(Src[i]);
          Inc(i);

          (* malformed trail byte or out of range char *)
          if (c and $C0) <> $80 then Exit;

          (* incomplete multibyte char *)
          if i >= SrcLen then Exit;

          wc := (wc shl 6) or (c and $3F);
        end;
        c := Byte(Src[i]);
        Inc(i);
        if (c and $C0) <> $80 then Exit;

        (* malformed trail byte *)
        Dest[count] := WideChar((wc shl 6) or (c and $3F));
      end
      else
        Dest[count] := WideChar(wc);
      Inc(count);
    end;
    //if (count >= destLen) then count := destLen;
    //dest[count] := #0;
  end
  else begin
    while i < SrcLen do
    begin
      c := Byte(Src[i]);
      Inc(i);
      if (c and $80) <> 0 then
      begin
        (* incomplete multibyte char *)
        if i >= SrcLen then Exit;

        c := c and $3F;
        if (c and $20) <> 0 then
        begin
          c := Byte(Src[i]);
          Inc(i);
          (* malformed trail byte or out of range char *)
          if (c and $C0) <> $80 then Exit;

          (* incomplete multibyte char *)
          if i >= SrcLen then Exit;
        end;
        c := Byte(Src[i]);
        Inc(i);

        (* malformed trail byte *)
        if (c and $C0) <> $80 then Exit;
      end;
      Inc(count);
    end;
  end;
  Result := count;
end;

function Utf8Encode(const s: WideString): UTF8String;
begin
  SetLength(Result, UnicodeToUtf8(PWideChar(s), Length(s), nil, 0));
  UnicodeToUtf8(PWideChar(s), Length(s), PAnsiChar(Result), Length(Result));
end;

function Utf8Decode(const s: UTF8String): WideString;
begin
  SetLength(Result, Utf8ToUnicode(PAnsiChar(s), Length(s), nil, 0));
  Utf8ToUnicode(PAnsiChar(s), Length(s), PWideChar(Result), Length(Result));
end;

function AnsiToUtf8(const s: AnsiString): UTF8String;
begin
  Result := Utf8Encode(s);
end;

function Utf8ToAnsi(const s: UTF8String): AnsiString;
begin
  Result := Utf8Decode(s);
end;

function wctomb(Src: PWideChar; SrcLen: TUnsignedLong; dest: PAnsiChar;
  destLen: TUnsignedLong; CodePage: Integer): TUnsignedLong;
begin
  Result := WideCharToMultiByte(CodePage, 0, Src, SrcLen,
    dest, destLen, nil, nil);
end;

function UnicodeToAnsi(const s: WideString; CodePage: Integer): AnsiString;
begin
  SetLength(Result, wctomb(PWideChar(Pointer(s)), Length(s), nil, 0, CodePage));
  wctomb(PWideChar(s), Length(s), PAnsiChar(Result), Length(Result), CodePage);
end;

function mbtowc(Src: PAnsiChar; SrcLen: TUnsignedLong; dest: PWideChar;
  destLen: TUnsignedLong; CodePage: Integer): TUnsignedLong;
begin
  Result := MultiByteToWideChar(CodePage, 0, Src, SrcLen, dest, destLen);
end;

function AnsiToUnicode(const s: AnsiString; CodePage: Integer): WideString;
begin
  SetLength(Result, mbtowc(PAnsiChar(s), Length(s), nil, 0, CodePage));
  mbtowc(PAnsiChar(s), Length(s), PWideChar(Result), Length(Result), CodePage);
end;

{ TAnsiCharRange }

function TAnsiCharRange.AsString: AnsiString;
begin
  Result := AnsiStrFromBuf(StrBegin, StrEnd - StrBegin);
end;

{ TWideCharRange }

function TWideCharRange.AsString: WideString;
begin
  Result := AnsiStrFromBuf(StrBegin, 2 * (StrEnd - StrBegin));
end;

{ TRefWideString }

procedure TRefWideString.Append(Str: PRefWideString);
begin
  if Assigned(Str) then Append(Str.AsBuffer, Str.GetLength);
end;

procedure TRefWideString.Append(Str: PWideChar; Size: Integer);
var
  OldLen: Integer;
begin
  if Size = 0 then Exit;
  OldLen := GetLength;
  CopyOnWrite(OldLen + Size, 1);
  Write(OldLen + 1, Str, Size);
end;

procedure TRefWideString.Append(const Str: WideString);
begin
  Append(PWideChar(Str), Length(Str));
end;

function TRefWideString.AsAnsiStr: AnsiString;
begin
  if GetLength = 0 then Result := ''
  else begin
    System.SetLength(Result, wctomb(AsBuffer, GetLength, nil, 0));
    wctomb(AsBuffer, GetLength, PAnsiChar(Result), Length(Result));
  end;
end;

function TRefWideString.AsBuffer: PWideChar;
begin
  Result := PWideChar(Pointer(RefRec));
end;

procedure TRefWideString.Assign(Str: PAnsiChar; Size: Integer);
begin
  SetLength(mbtowc(Str, Size, nil, 0));
  mbtowc(Str, Size, AsBuffer, GetLength);
end;

procedure TRefWideString.AssignUTF8(Str: PAnsiChar; Size: Integer);
begin
  SetLength(Utf8ToUnicode(Str, Size, nil, 0));
  Utf8ToUnicode(Str, Size, AsBuffer, GetLength);
end;

function TRefWideString.AsUTF8Str: AnsiString;
begin
  if GetLength = 0 then Result := ''
  else begin
    System.SetLength(Result, UnicodeToUtf8(AsBuffer, GetLength, nil, 0));
    UnicodeToUtf8(AsBuffer, GetLength, PAnsiChar(Result), Length(Result));
  end;
end;

procedure TRefWideString.Assign(const Str: AnsiString);
begin
  Assign(PAnsiChar(Str), Length(Str));
end;

procedure TRefWideString.Assign(Str: PWideChar; Size: Integer);
begin
  if Str = AsBuffer then Exit;
  CopyOnWrite(Size, 0);
  Write(1, Str, Size);
end;

procedure TRefWideString.Assign(const Str: TRefWideString);
begin
  if @Str <> @Self then
  begin
    CopyOnWrite(0, 0);
    RefRec := Str.RefRec;
  end;
end;

function TRefWideString.AsWideStr: WideString;
begin
  if Pointer(RefRec) = nil then Result := ''
  else with GetRefRec^ do
    begin
      System.SetLength(Result, (CharCount - 1) div 2);
      Move(CharArray, Pointer(Result)^, CharCount - 1);
    end;
end;

function TRefWideString.Copy(Index, Count: Integer): TRefWideString;
begin
  if Count > GetLength - Index + 1 then Count := GetLength - Index + 1;
  if Count > 0 then
  begin
    Result.SetLength(Count);
    Result.Write(1, GetRefRec.CharArray + Index - 1, Count);
  end;
end;

procedure TRefWideString.CopyOnWrite(NewLen, CopyPos: Integer);
var
  OldStr: AnsiString;
  Rec: PWideStrRefRec;
  OldLen: Integer;
begin
  if RefRec = '' then
  begin
    Rec := nil;
    OldLen := 0;
  end
  else begin
    Rec := PWideStrRefRec(PAnsiChar(RefRec) - 8);
    OldLen := (Rec.CharCount - 1) div 2;
  end;
  if NewLen = -1 then NewLen := OldLen;
  OldStr := RefRec;
  if not Assigned(Rec) or (Rec.RefCount = 1) then
  begin
    if NewLen <> OldLen then
    begin
      if NewLen = 0 then RefRec := ''
      else begin
        System.SetLength(RefRec, NewLen * 2 + 1);
        Rec := PWideStrRefRec(PAnsiChar(RefRec) - 8);
        PWideChar(@Rec^.CharArray)[NewLen] := #0;
        if (CopyPos > 0) and (OldStr <> '') then
          Self.Write(CopyPos, PWideChar(Pointer(OldStr)), OldLen);
      end;
    end;
  end
  else begin
    if NewLen = 0 then RefRec := ''
    else begin
      System.SetLength(RefRec, NewLen * 2 + 1);
      Rec := PWideStrRefRec(PAnsiChar(RefRec) - 8); ;
      PWideChar(@Rec^.CharArray)[NewLen] := #0;
      if (CopyPos > 0) and (OldStr <> '') then
        Self.Write(CopyPos, PWideChar(Pointer(OldStr)), OldLen);
    end;
  end;
  OldStr := '';
end;

procedure TRefWideString.Delete(Index, Count: Integer);
var
  Temp: TRefWideString;
begin
  if Count > GetLength - Index + 1 then Count := GetLength - Index + 1;
  if Count > 0 then
    with GetRefRec^ do
    begin
      Temp.SetLength(GetLength - Count);
      Temp.Write(1, CharArray, Index - 1);
      Temp.Write(Index, CharArray + Index + Count - 1, MaxInt);
      Self.Assign(Temp);
    end;
end;

function TRefWideString.Empty: Boolean;
begin
  Result := GetLength = 0;
end;

function TRefWideString.GetAt(Index: Integer): WideChar;
begin
  Result := PWideChar(@GetRefRec.CharArray)[Index - 1];
end;

function TRefWideString.GetLength: Integer;
begin
  if Pointer(RefRec) = nil then Result := 0
  else Result := (GetRefRec^.CharCount - 1) div 2;
end;

function TRefWideString.GetRefRec: PWideStrRefRec;
begin
  if Pointer(RefRec) = nil then Result := nil
  else Result := PWideStrRefRec(LongInt(RefRec) - 8);
end;

procedure TRefWideString.Insert(const Str: WideString);
begin
  Insert(PWideChar(Str), Length(Str));
end;

procedure TRefWideString.Insert(Str: PRefWideString);
begin
  if Assigned(Str) then Insert(Str.AsBuffer, Str.GetLength);
end;

procedure TRefWideString.Insert(Str: PWideChar; Size: Integer);
var
  OldLen: Integer;
begin
  if Size = 0 then Exit;
  OldLen := GetLength;
  CopyOnWrite(OldLen + Size, Size + 1);
  Write(1, Str, Size);
end;

procedure TRefWideString.SetAt(Index: Integer; Value: WideChar);
begin
  if Value = PWideChar(@GetRefRec.CharArray)[Index - 1] then Exit;
  CopyOnWrite(GetLength, 1);
  PWideChar(@GetRefRec.CharArray)[Index - 1] := Value;
end;

procedure TRefWideString.SetLength(NewLen: Integer);
begin
  CopyOnWrite(NewLen, 1);
end;

procedure TRefWideString.Write(From: Integer; Buf: PWideChar; Size: Integer);
begin
  if Size > GetLength - From + 1 then Size := GetLength - From + 1;
  Move(Buf^, PWideChar(Pointer(RefRec))[From - 1], Size * 2);
end;

procedure TRefWideString.Assign(const Str: WideString);
begin
  Assign(PWideChar(Str), Length(Str));
end;

procedure TRefWideString.AssignUTF8(const Str: UTF8String);
begin
  AssignUTF8(PAnsiChar(Str), Length(Str));
end;

function CompareCodePageName(Name1, Name2: PAnsiChar;
  Len1, Len2: Integer): Integer; overload;
var
  i: Integer;
  C1, C2: AnsiChar;
begin
  if Len1 > Len2 then Result := 1
  else if Len1 < Len2 then Result := -1
  else begin
    for i := Len1 - 1 downto 0 do
    begin
      C1 := Name1[i];
      if C1 in ['A'..'Z'] then C1 := AnsiChar(Byte(C1) or $20);
      C2 := Name2[i];
      if C2 in ['A'..'Z'] then C2 := AnsiChar(Byte(C2) or $20);
      if C1 = C2 then Continue;
      if C1 > C2 then Result := 1
      else Result := -1;
      Exit;
    end;
    Result := 0;
  end;
end;

function CompareCodePageName(const Name1, Name2: AnsiString): Integer; overload;
begin
  Result := CompareCodePageName(PAnsiChar(Name1), PAnsiChar(Name2),
    Length(Name1), Length(Name2));
end;

type
  TAnsiStrPtrAndLen = record
    Ptr: PAnsiChar;
    Len: Integer;
  end;
  PAnsiStrPtrAndLen = ^TAnsiStrPtrAndLen;
  
function CodePageNameSearchCmp(First: PCodePage; Second: PAnsiStrPtrAndLen): Integer;
begin
  Result := CompareCodePageName(PAnsiChar(First^.Name), Second^.Ptr,
    Length(First^.Name), Second^.Len);
end;

function GetCodePageID(Name: PAnsiChar; NameLen: Integer): Integer;
var
  PtrLen: TAnsiStrPtrAndLen;
begin
  PtrLen.Ptr := Name; PtrLen.Len := NameLen;
  Result := BinarySearch(@NAME_SORTED_CODE_PAGES, SizeOf(TCodePage),
    Length(NAME_SORTED_CODE_PAGES), @CodePageNameSearchCmp, PtrLen);
  if Result >= 0 then Result := NAME_SORTED_CODE_PAGES[Result].ID;
end;

function GetCodePageID(const Name: AnsiString): Integer;
begin
  Result := GetCodePageID(PAnsiChar(Name), Length(Name));
end;

function CodePageIDSearchCmp(First: PCodePage; Second: PInteger): Integer;
begin
  Result := First^.ID - Second^;
end;

function GetCodePageName(ID: Integer): AnsiString; overload;
var
  Index: Integer;
begin
  Index := BinarySearch(@ID_SORTED_CODE_PAGES, SizeOf(TCodePage),
    Length(ID_SORTED_CODE_PAGES), @CodePageIDSearchCmp, ID);
  if Index >= 0 then Result := ID_SORTED_CODE_PAGES[Index].Name
  else Result := '';
end;

procedure SwapCodePage(First, Second: PCodePage);
var
  Temp: TCodePage;
begin
  Temp := First^;
  First^ := Second^;
  Second^ := Temp;
end;

function CompareCodePageByName(First, Second: PCodePage): Integer;
begin
  Result := CompareCodePageName(First^.Name, Second^.Name);
end;

function CompareCodePageByID(First, Second: PCodePage): Integer;
begin
  Result := First^.ID - Second^.ID;
end;

{ TDzAnsiStringBuilder }

procedure TDzAnsiStringBuilder.FormatWrite(const fmt: AnsiString; const args: array of const);
begin
  WriteString(Format(fmt, args));
end;

procedure TDzAnsiStringBuilder.FormatWriteBuf(const fmt: PAnsiChar; FmtLen: Integer;
  args: array of const);
var
  CharWrites: Integer; 
begin
  CharWrites := FormatBuf(fDataString[fPosition + 1],
    Self.Length - Position, fmt^, FmtLen, args);
  Inc(fPosition, CharWrites);
end;

function TDzAnsiStringBuilder.getLength: Integer;
begin
  Result := System.Length(fDataString);
end;

procedure TDzAnsiStringBuilder.init;
begin
  fPosition := 0;
end;

procedure TDzAnsiStringBuilder.SetDataString(const Value: AnsiString);
begin
  fDataString := Value;
  if System.Length(fDataString) < fPosition then fPosition := System.Length(fDataString);
end;

procedure TDzAnsiStringBuilder.setLength(newLen: Integer);
begin
  System.SetLength(fDataString, newLen);
  if newLen < fPosition then fPosition := newLen;
end;

procedure TDzAnsiStringBuilder.setPosition(const Value: Integer);
begin
  fPosition := Value;
  if fPosition < 0 then fPosition := 0
  else if fPosition > Self.Length then
    fPosition := Self.Length;
end;

procedure TDzAnsiStringBuilder.uninit;
begin
  fDataString := '';
  fPosition := 0;
end;

procedure TDzAnsiStringBuilder.WriteBuffer(buf: PAnsiChar; len: Integer);
begin
  if len + fPosition > Self.Length then
    Self.setLength(len + fPosition);
  Move(buf^, (PAnsiChar(fDataString) + fPosition)^, len);
  Inc(fPosition, len);
end;

procedure TDzAnsiStringBuilder.WriteInt(value: Integer);
var
  buf: array [0..15] of AnsiChar;
  n: Integer;
  neg: Boolean;
begin
  if value < 0 then
  begin
    neg := True;
    value := -value;
  end
  else neg := False;
  n := 15;
  repeat
    buf[n] := AnsiChar(value mod 10 + $30);
    value := value div 10;
    Dec(n);
  until value = 0;
  if neg then
  begin
    buf[n] := '-';
    Dec(n);
  end;
  WriteBuffer(@buf[n + 1], 15 - n);
end;

procedure TDzAnsiStringBuilder.WriteString(const str: AnsiString);
begin
  WriteBuffer(PAnsiChar(str), System.Length(str));
end;

initialization
  uDZAlgorithm.QuickSort(@NAME_SORTED_CODE_PAGES, SizeOf(TCodePage),
    Length(NAME_SORTED_CODE_PAGES), @CompareCodePageByName, @SwapCodePage);
  uDZAlgorithm.QuickSort(@ID_SORTED_CODE_PAGES, SizeOf(TCodePage),
    Length(ID_SORTED_CODE_PAGES), @CompareCodePageByID, @SwapCodePage);
end.

